package com.ebspp.client.serverManager;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransportException;

import com.ebspp.global.General;
import com.ebspp.thrift.DataMsg;
import com.ebspp.thrift.Identity;
import com.ebspp.thrift.ResourceType;

/**
 * manages all the read connections
 * @author x23pan
 *
 */
public class ReadConnManager 
{
	int rPort;
	IStat stat;
	Map<Integer, Map<String, ReadConnection>> rConnMap;
	
	public ReadConnManager(IStat stat)
	{
		this.rPort = General.rPort;
		this.stat = stat;
		rConnMap = Collections.synchronizedMap(new HashMap<Integer, Map<String, ReadConnection>>());
	}
	

	public synchronized ReadConnection createRConn(String server, ResourceType resType, 
														int bufID, Identity clientID)
	{		
		if(rConnMap.get(bufID) == null)
			rConnMap.put(bufID, Collections.synchronizedMap(new HashMap<String, ReadConnection>()));
		Map<String, ReadConnection> serverRConn = rConnMap.get(bufID);
		if(serverRConn.get(server) != null)
			return serverRConn.get(server);
		
		
		ReadConnection rConn = null;
		try 
		{
			rConn = new ReadConnection(server, rPort, resType, clientID, stat);
			rConn.connect();
			
			serverRConn.put(server, rConn);
		} 
		catch (TTransportException e) {
			// TODO Auto-generated catch block
			System.err.println("Unable to init read connection to " + server + "\n" + e.getMessage());
			e.printStackTrace();
			if(serverRConn.size() == 0)
				rConnMap.remove(bufID);
			return null;
		}
		
		return rConn;
	}
	
	public synchronized boolean closeRConn(int bufID, String server)
	{
		if(rConnMap.get(bufID) == null)
			return true;
		Map<String, ReadConnection> serverRConn = rConnMap.get(bufID);
		if(serverRConn.get(server) == null)
			return true;
		
		ReadConnection rConn = serverRConn.get(server);
		rConn.close();
		
		serverRConn.remove(server);
		if(serverRConn.size() == 0)
			rConnMap.remove(bufID);
		
		return true;
	}
	
	public String provision(String server, int bufID, int amount)
	{
		if(rConnMap.get(bufID) == null)
			return null;
		Map<String, ReadConnection> serverConnMap = rConnMap.get(bufID);
		if(serverConnMap.get(server) == null)
			return null;
		
		ReadConnection rConn = serverConnMap.get(server);
		try {
			return rConn.provision(amount);
		} 
		catch (TException e) {
			// TODO Auto-generated catch block
			System.err.println("Provision call failure to " + rConn.getServer() +"\n" + e.getMessage());
			e.printStackTrace();
			return null;
		}
	}
	
	public List<DataMsg> read(List<DataMsg> data, String server, int bufID) throws TException
	{
		Map<String, ReadConnection> serverConnMap = rConnMap.get(bufID);	
		if(serverConnMap == null)
			return null;
		ReadConnection rConn = serverConnMap.get(server);
		if(rConn == null)
			return null;
		
		return rConn.read(data);
	}
	
	
	public int getBufUsage(int bufID)
	{
		int usage = 0;
		
		Map<String, ReadConnection> serverConnMap = rConnMap.get(bufID);
		if(serverConnMap == null)
			return 0;
		
		Iterator<ReadConnection> it = serverConnMap.values().iterator();
		synchronized(serverConnMap)
		{
			while(it.hasNext())
			{
				ReadConnection rConn = it.next();
				usage += getBufServerUsage(rConn.getServer(), bufID);
			}
		}
		return usage;
	}
	
	public int getBufServerUsage(String server, int bufID)
	{
		return Math.abs(getBufServerRes(server, bufID));
	}
	
	public int getBufServerRes(String server, int bufID)
	{
		Map<String, ReadConnection> bufConn = rConnMap.get(bufID);
		if(bufConn == null)
			return 0;
		
		ReadConnection rConn = bufConn.get(server);
		if(rConn == null)
			return 0;
		return rConn.getResource();
	}
}
